package edu.uci.ics.jung.algorithms.shortestpath;

import java.util.Collection;
import java.util.Set;

import org.apache.commons.collections15.Factory;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.functors.ConstantTransformer;

import edu.uci.ics.jung.algorithms.cluster.WeakComponentClusterer;
import edu.uci.ics.jung.algorithms.filters.FilterUtils;
import edu.uci.ics.jung.graph.Forest;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.Tree;
import edu.uci.ics.jung.graph.util.TreeUtils;

/**
 * For the input Graph, creates a MinimumSpanningTree using a variation of
 * Prim's algorithm.
 * 
 * @author Tom Nelson - tomnelson@dev.java.net
 *
 * @param <V>
 * @param <E>
 */
@SuppressWarnings("unchecked")
public class MinimumSpanningForest2<V, E> {

	protected Graph<V, E> graph;
	protected Forest<V, E> forest;
	protected Transformer<E, Double> weights = (Transformer<E, Double>) new ConstantTransformer<Double>(
			1.0);

	/**
	 * create a Forest from the supplied Graph and supplied Factory, which is
	 * used to create a new, empty Forest. If non-null, the supplied root will
	 * be used as the root of the tree/forest. If the supplied root is null, or
	 * not present in the Graph, then an arbitary Graph vertex will be selected
	 * as the root. If the Minimum Spanning Tree does not include all vertices
	 * of the Graph, then a leftover vertex is selected as a root, and another
	 * tree is created
	 * 
	 * @param graph
	 * @param factory
	 * @param weights
	 */
	public MinimumSpanningForest2(Graph<V, E> graph,
			Factory<Forest<V, E>> factory,
			Factory<? extends Graph<V, E>> treeFactory,
			Transformer<E, Double> weights) {
		this(graph, factory.create(), treeFactory, weights);
	}

	/**
	 * create a forest from the supplied graph, populating the supplied Forest,
	 * which must be empty. If the supplied root is null, or not present in the
	 * Graph, then an arbitary Graph vertex will be selected as the root. If the
	 * Minimum Spanning Tree does not include all vertices of the Graph, then a
	 * leftover vertex is selected as a root, and another tree is created
	 * 
	 * @param graph
	 *            the Graph to find MST in
	 * @param forest
	 *            the Forest to populate. Must be empty
	 * @param weights
	 *            edge weights, may be null
	 */
	public MinimumSpanningForest2(Graph<V, E> graph, Forest<V, E> forest,
			Factory<? extends Graph<V, E>> treeFactory,
			Transformer<E, Double> weights) {

		if (forest.getVertexCount() != 0) {
			throw new IllegalArgumentException("Supplied Forest must be empty");
		}
		this.graph = graph;
		this.forest = forest;
		if (weights != null) {
			this.weights = weights;
		}

		WeakComponentClusterer<V, E> wcc = new WeakComponentClusterer<V, E>();
		Set<Set<V>> component_vertices = wcc.transform(graph);
		Collection<Graph<V, E>> components = FilterUtils
				.createAllInducedSubgraphs(component_vertices, graph);

		for (Graph<V, E> component : components) {
			PrimMinimumSpanningTree<V, E> mst = new PrimMinimumSpanningTree<V, E>(
					treeFactory, this.weights);
			Graph<V, E> subTree = mst.transform(component);
			if (subTree instanceof Tree) {
				TreeUtils.addSubTree(forest, (Tree<V, E>) subTree, null, null);
			}
		}
	}

	/**
	 * Returns the generated forest.
	 */
	public Forest<V, E> getForest() {
		return forest;
	}
}
